﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Citrix.Cmp;
using Citrix.XaAdminMobile.ViewModel;

namespace Citrix.XaAdminMobile
{
    /// <summary>
    /// The tablet layout navigation implementation.
    /// </summary>
    class TabletNavigation : INavigation
    {
        /// <summary>
        /// The page changed event handlers.
        /// </summary>
        private List<WeakReference> eventHandlers;

        /// <summary>
        /// The page to display in the left hand column.
        /// </summary>
        private IPage leftPage;

        /// <summary>
        /// The page to display in the right hand column.
        /// </summary>
        private IPage rightPage;

        /// <summary>
        /// A reference back to the root v-m object.
        /// </summary>
        private readonly ViewModelManager vmRoot;

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="vmManager">The root v-m object.</param>
        /// <param name="startingPage">The starting page.</param>
        /// <exception cref="ArgumentNullException">Thrown if any parameter is null.</exception>
        internal TabletNavigation(ViewModelManager vmManager, IPage startingPage)
        {
            if (null == vmManager)
            {
                throw new ArgumentNullException("vmManager");
            }
            if (null == startingPage)
            {
                throw new ArgumentNullException("currentPhonePage");
            }

            this.vmRoot = vmManager;
            this.vmRoot.SessionsViewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SessionsViewModel_PropertyChanged);
            NavigateForward(startingPage);
        }

        /// <summary>
        /// The page to display in the left-hand column.
        /// </summary>
        public IPage LeftPage
        {
            get { return this.leftPage; }
        }

        /// <summary>
        /// The page to display in the right-hand column.
        /// </summary>
        public IPage RightPage
        {
            get { return this.rightPage; }
        }

        /// <summary>
        /// Returns the current back button navigation target that should be applied based on
        /// the navigation state.
        /// </summary>
        public Cmp.ButtonTarget BackButtonTarget
        {
            get { return Cmp.ButtonTarget.ClientDevice; }
        }

        /// <summary>
        /// Converts the current navigation state of this layout to a different layout.
        /// </summary>
        /// <param name="layout">The target navigation layout.</param>
        /// <returns>The new navigation implementation.</returns>
        public INavigation Convert(NavigationLayout layout)
        {
            if (NavigationLayout.Tablet == layout)
            {
                return this;
            }
            else
            {
                if (null != this.rightPage)
                {
                    return new PhoneNavigation(this.vmRoot, this.rightPage);
                }
                else
                {
                    return new PhoneNavigation(this.vmRoot, this.leftPage);
                }
            }
        }

        /// <summary>
        /// Checks whether or not the specified page is being displayed.
        /// </summary>
        /// <param name="page">The page to check.</param>
        /// <returns>True if visible, false if not.</returns>
        public bool IsActive(IPage page)
        {
            return object.ReferenceEquals(this.rightPage, page) || object.ReferenceEquals(this.leftPage, page);
        }

        /// <summary>
        /// Performs a back navigation operation to the previous page in the navigation history.
        /// </summary>
        /// <remarks>
        /// Most of the tablet nav hierarchy is flat due to the two column layout, so we don't
        /// support much back navigation. We only support it in some specific cases.
        /// </remarks>
        public void NavigateBack()
        {
            // Navigating back from the add server page will clear the add server page from
            // the right column, and leave the settings page displayed on the left.
            if (this.rightPage is AddServerViewModel)
            {
                NavigateForward(this.vmRoot.SettingsViewModel);
            }
        }

        /// <summary>
        /// Requests that the specified page be displayed.
        /// </summary>
        /// <param name="page">The page to display.</param>
        public void NavigateForward(IPage page)
        {
            Type current = page.GetType();
            IPage left = null;
            IPage right = null;
            if (typeof(SessionsViewModel) == current)
            {
                left = page;
                right = this.vmRoot.SessionsViewModel.GetDefaultSession();
            }
            else if (typeof(SessionViewModel) == current)
            {
                left = this.vmRoot.SessionsViewModel;
                right = page;
            }
            else if (typeof(ServersViewModel) == current)
            {
                left = page;
                right = this.vmRoot.ServersViewModel.GetDefaultServer();
            }
            else if (typeof(ServerViewModel) == current)
            {
                left = this.vmRoot.ServersViewModel;
                right = page;
            }
            else if (typeof(SettingsViewModel) == current)
            {
                left = page;
            }
            else if (typeof(AddServerViewModel) == current)
            {
                left = this.vmRoot.SettingsViewModel;
                right = page;
            }

            bool pageChanged = false;
            if (!object.ReferenceEquals(this.leftPage, left))
            {
                if (null != this.leftPage)
                {
                    this.leftPage.NotifyActiveChanged(false);
                }
                left.NotifyActiveChanged(true);
                this.leftPage = left;
                pageChanged = true;
            }

            if (!object.ReferenceEquals(this.rightPage, right))
            {
                if (null != this.rightPage)
                {
                    this.rightPage.NotifyActiveChanged(false);
                }
                if (null != right)
                {
                    right.NotifyActiveChanged(true);
                }
                this.rightPage = right;
                pageChanged = true;
            }

            if (pageChanged)
            {
                WeakRefEventHelper.CallWeakReferenceHandlers(this.eventHandlers, this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handler for a physical button press on the device.
        /// </summary>
        /// <param name="buttonId">The button that was pressed.</param>
        public void NotifyButtonPressed(Cmp.ButtonId buttonId)
        {
            // Do nothing, we currently don't support back presses in the tablet layout due to
            // the flat navigation hierarchy.
        }

        /// <summary>
        /// Fired whenever the page(s) to be displayed to the user have changed.
        /// </summary>
        /// <remarks>
        /// Implementers are assumed to implement this explicitly using weak references. If they
        /// don't then there will be a memory leak when switching between layouts.
        /// </remarks>
        public event EventHandler PageChanged
        {
            add
            {
                WeakRefEventHelper.AddWeakReferenceHandler(ref this.eventHandlers, value);
            }
            remove
            {
                WeakRefEventHelper.RemoveWeakReferenceHandler(this.eventHandlers, value);
            }
        }

        /// <summary>
        /// Handler for changes to the SessionsViewModel instance. We detect changes to the
        /// list of sessions so we can trigger auto navigation actions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SessionsViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SessionsMaster")
            {
                //
                // We check for the sessions changing so we can default select a session in the
                // right hand column at appropriate times, e.g. sessions have been loaded when
                // there were no sessions before; the previously selected session has ended.
                //
                if (this.leftPage is SessionsViewModel)
                {
                    SessionViewModel session = this.vmRoot.SessionsViewModel.GetDefaultSession();
                    if (null != session && !object.ReferenceEquals(session, this.rightPage))
                    {
                        NavigateForward(session);
                    }
                }
            }
        }
    }
}
